home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Mac Game Programming Gurus
/
TricksOfTheMacGameProgrammingGurus.iso
/
More Source
/
Libraries
/
VideoToolbox 95.04.18
/
VideoToolboxSources
/
GDVideo.c
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
UTF-8
Wrap
Text File
|
1995-04-11
|
60.4 KB
|
1,630 lines
|
[
TEXT/MMCC
]
/*
GDVideo.c
Copyright © 1989-1994 Denis G. Pelli
Complete set of routines to control video drivers directly, bypassing
QuickDraw's Color and Palette Managers. There is a separate function call for
each of the Control and Status Calls (csc) implemented by video drivers. They
are described in Designing Cards and Drivers, 3rd Ed., chapter 9. A few new
calls are documented in the Display Device Driver Guide Developer Note, which
appeared on the January '94 Developer CD.
This file also contains several other helpful routines that deal with video
device drivers. For background, read “Video synch”.
THE MORE-USEFUL (HIGH-LEVEL) ROUTINES: (in alphabetical order)
char *GDCardName(GDHandle device);
Returns the address of a C string containing the name of the video card. You
should call DisposePtr() on the returned string when you no longer need it.
Takes about 1.5 ms because Apple's slot routines are very slow.
short GDClutSize(GDHandle device);
Returns the number of entries in the video driver's clut in the current video
mode (i.e. current pixel size). VideoToolbox.h defines a preprocessor macro
GDCLUTSIZE(device) that expands to equivalent inline code.
long GDColors(GDHandle device)
Number of colors, in the current mode.
short GDDacSize(GDHandle device)
Figures out how many bits in the video dac.
OSErr GDGetDisplayMode(GDHandle device,unsigned long *displayModeIDPtr
,unsigned short *modePtr,unsigned short *pagePtr,Ptr *baseAddrPtr);
Calls cscGetCurMode, documented in Apple's new Display Device Driver Guide
Developer Note on the January '94 Developer CD. The "display mode ID" is a new
parameter, to support multi-resolution displays. Each display mode may have
multiple pixel depths (which, confusingly, have also been called "modes"). This
call will only be useful if the Display Manager is present (requires PowerPC or
System 7.5) and if you're using the Display Manager, which is documented in a
chapter in Inside Macintosh:Advanced Color Imaging, which thus far has appeared
only in draft form on the December '94 Developer CD. The call returns an error
if the video driver does not support the Display Manager. UNTESTED!!
OSErr GDGetEntries(GDHandle device,short start,short count,ColorSpec *table);
Calls cscGetEntries. This is much as you'd expect after reading GDSetEntries
below. Note that unless the gamma table is linear, the values returned may not
be the same as those originally passed by GDSetEntries. So call
GDUncorrectedGamma first. Try the demo TimeVideo.
OSErr GDGetGamma(GDHandle device,GammaTbl **myGammaTblHandle);
Calls cscGetGamma. Returns a pointer to the Gamma table in the specified video
device. (I.e. you pass it a pointer to your pointer, a handle, which it uses to
load your pointer.)
OSErr GDGetPageCnt(GDHandle device,short mode,short *pagesPtr);
Calls cscGetPageCnt. Called "GetPages" in Designing Cards and Drivers, 3rd Ed.
You tell it which mode you're interested in. It tells you how many pages of
video ram are available in that mode.
Boolean GDHasMode(GDHandle device,short mode,short *pixelSizePtr,short *pagesPtr);
Returns 0 if no such video mode, returns 1 if mode is available.
If pixelSizePtr is not NULL, then sets *pixelSizePtr to pixelSize or -1 if unknown.
If pagesPtr is not NULL, then sets *pagesPtr to pages.
unsigned char *GDName(GDHandle device);
Returns a pointer to the name of the driver (a pascal string). This is quick.
The string is part of the driver itself, so don't modify it or try to dispose of it.
unsigned char *GDNameStr(GDHandle device);
Returns a pointer to the name of the driver, as a C string.
The string is static, and will be overwritten by the next call to GDNameStr().
ColorSpec *GDNewLinearColorTable(GDHandle device);
Creates a default table for use when gdType==directType.
OSErr GDPrintGammaTable(FILE *o,GDHandle device);
OSErr GDRestoreDeviceClut(GDHandle device);
Nominally equivalent to Apple's RestoreDeviceClut(), which is only available
since System 6.05. However, I find that Apple's routine sometimes does nothing,
whereas this routine always works. Passing a NULL argument causes restoration of
cluts of all video devices.
OSErr GDSaveGamma(GDHandle device);
OSErr GDRestoreGamma(GDHandle device);
Use an internal cache to save and restore the device's gamma-correction table. Call
GDRestoreGamma before GDRestoreDeviceClut.
OSErr GDSetEntries(GDHandle device,short start,short count,ColorSpec *table);
Does a cscSetEntries call to the video card's driver, loading any
number of clut entries with arbitrary rgb triplets. (Note that the driver will
transform your rgb triplets via the gamma table before loading them into the
clut; so call GDUncorrectedGamma first.) "device" specifies which video device's
clut you want to load. "start" is either in the range 0 to clutSize-1,
indicating which clut entry to load first (in "sequence mode"), or is -1
(requesting "index mode"). "count" is the number of entries to be modified,
minus 1. "table" is a ColorSpec array (not a ColorTable) containing the rgb
triplets that you want to load into the clut. In sequence mode "start" must be
in the range 0 to clutSize-1, the i-th element of table corresponds to the
i+start entry in the clut, and the "value" field of each element of table is
ignored. In index mode "start" must be -1, and the "value" field of each element
of table indicates which clut entry to load it into. The arguments start, count,
and table are the same as for the Color Manager call SetEntries(), documented in
Inside Macintosh V-143. (Most drivers wait for blanking before modifying the
clut. For a full discussion, read the VideoToolbox "Video synch" file.) You may
also want to look at the file SetEntriesQuickly.c, which provides the
functionality of GDSetEntries and GDDirectSetEntries, but bypasses the video
driver to access the hardware directly.
OSErr GDSetEntriesByType(GDHandle device,short start,short count,ColorSpec *table);
Checks the (**device).gdType field and calls cscSetEntries, cscDirectSetEntries,
or nothing, as appropriate.
OSErr GDSetEntriesByTypeHighPriority(GDHandle device,short start,short count
,ColorSpec *table);
Calls GDSetEntriesByType() while the processor priority has been temporarily
raised to 7.
OSErr GDSetGamma(GDHandle device, GammaTbl *gamma);
Calls cscSetGamma. Loads a Gamma table into the specified video device. The
video driver will make a copy of your table. You can discard your table after
making this call. Note that the video driver only uses the gamma table when
performing SetEntries, i.e. when actually loading the clut. The structure of the
gamma table is (finally!) documented in Designing Cards and Drivers, 3rd
edition, pages 215-216. Beware of a discrepancy between the documentation and
the definition in QuickDraw.h: gFormulaData is described as a byte array in the
text, but is declared as a short array in the QuickDraw.h header file. NOTE: a
few video drivers (Radius PowerView and SuperMac ColorCard) do not support gamma
tables. The SuperMac ColorCard is well behaved, giving the appropriate error
code, returned by GDSetGamma and GDGetGamma. The Radius PowerView reports no
error yet ignores the GDSetGamma call and returns a table full of zeroes in
response to GDGetGamma. See NOTE from Tom Busey below. However, if all you want
to do is call GDUncorrectedGamma, then these limitations won't affect you,
because the drivers that lack gamma tables always give you precisely the
behaviour that one requests by calling GDUncorrectedGamma.
OSErr GDSetPageDrawn(GDHandle device,short page);
Choose which page of video memory will be used by future drawing operations.
Untested.
OSErr GDSetPageShown(GDHandle device,short page);
Choose which page of video memory we see. Untested.
OSErr GDUncorrectedGamma(GDHandle device);
Asks GDSetGamma to load a linear gamma table, i.e. no correction. (The gamma
correction implemented by table-lookup in the video driver is too crude for
experiments that want accurate luminance control.)
int GDVersion(GDHandle device)
Returns the version number of the driver. From the first word-aligned word after
the name string. This is quick.
OSErr GDGetNextResolution()
Implements cscGetNextResolution.
LESS-USEFUL (LOW LEVEL) ROUTINES:
OSErr GDControl(int refNum,int csCode,Ptr csParamPtr)
Uses low-level PBControl() call to implement a "Control()" call that works!
I don't know why this wasn't discussed in Apple Tech Note 262.
OSErr GDDirectSetEntries(GDHandle device,short start,short count,ColorSpec *table);
Calls cscDirectSetEntries. If your pixel depth is >8 then the cscSetEntries call is
disabled, and you must use this instead of GDSetEntries().
VideoDriver *GDDriverAddress(GDHandle device);
Returns a pointer to the driver, whether in ROM or RAM. Neither this prototype
nor the definition of the VideoDriver structure are included in VideoToolbox.h.
OSErr GDGetDefaultMode(GDHandle device,short *modePtr)
Calls cscGetDefaultMode. It tells you what the default mode is. I'm not sure
what this means.
OSErr GDGetGray(GDHandle device,Boolean *flagPtr);
Calls cscGetGray. Get gray flag. 0 for color. 1 if all colors mapped to
luminance-equivalent gray tones.
OSErr GDGetInterrupt(GDHandle device,Boolean *flagPtr);
Calls cscGetInterrupt. Get flag. 1 if VBL interrupts of this card are enabled. 0
if disabled.
OSErr GDGetMode(GDHandle device,short *modePtr,short *pagePtr,Ptr *baseAddrPtr);
Calls cscGetMode. It tells you the current mode, page of video memory, and the
base address of that page.
OSErr GDGetPageBase(GDHandle device,short page,Ptr *baseAddrPtr);
Calls cscGetPageBase. (Called "cscGetBaseAddr" in Designing Cards and Drivers, 3rd
Ed.) You tell it which page of video memory you're interested in (in the current
video mode). It tells you the base address of that page.
OSErr GDGrayPage(GDHandle device,short page);
Calls cscGrayPage. (Called "cscGrayScreen" in Designing Cards and Drivers, 3rd
Ed.) Fills the specified page with gray, i.e. the dithered desktop pattern. I'm
not aware of any particular advantage in using this instead of FillRect().
Designing Cards and Drivers, 3rd Edition, Chapter 9, says that for direct
devices, i.e. >8 bit pixels, the driver will also load a linear,
gamma-corrected, gray color table.
OSErr GDReset(GDHandle device, short *modePtr, short *pagePtr, Ptr *baseAddrPtr);
Calls cscReset. Initialize the video card to its startup state, usually 1 bit
per pixel. Returns the parameters of that state.
OSErr GDSetDefaultMode(GDHandle device,short mode);
Calls cscSetDefault. Supposedly, you tell it what mode you want to start up with
when you reboot. (I've never been able to get this to work. No error and no
effect. Perhaps I've misunderstood its purpose.)
OSErr GDSetGray(GDHandle device,Boolean flag);
Calls cscSetGray. Set gray flag. 0 for color. 1 if all colors mapped to
luminance-equivalent gray tones.
OSErr GDSetInterrupt(GDHandle device,Boolean flag);
Calls cscSetInterrupt. Set flag to 1 to enable VBL interrupts of this card, or 0
to disable.
OSErr GDSetMode(GDHandle device,short mode,short page,Ptr *baseAddrPtr);
Calls cscSetMode. You tell it what mode and video page you want. It sets 'em and
returns the base address of that page in video memory. Also, because Apple said
so, the video driver sets the entire clut to 50% gray. Note that this changes
things behind QuickDraw's back. For most applications life will be simpler if
you overtly ask QuickDraw to take charge by calling Apple's SetDepth() instead
of GDSetMode(). WARNING: Apple now considers the mode numbers merely ordinal.
E.g. on the 8•24 video card the 32-bit mode has mode number 0x84, not 0x85 as
you might have expected on the basis of old Apple documentation and other Apple
video cards.
OSErr GDStatus(int refNum,int csCode,Ptr csParamPtr)
Uses low-level PBStatus() call to implement a "Status()" call that works! The
need for this is explained in Apple Tech Note 262, which was issued in response
to my bug report in summer of '89.
PatchMacIIciVideoDriver();
Fixes a crashing bug in the Mac IIci built-in video driver's implementation of
cscGetEntries. The patch persists until reboot. It is unlikely that you will need
to call this explicitly, PatchMacIIciVideoDriver() is automatically invoked the
first time GDGetEntries is called. The Mac IIci built-in video driver
(.Display_Video_Apple_RBV1 driver, version 0) has a bug that causes it to crash
if you try to do a GetEntries Status request. PatchMacIIciVideoDriver() finds and
patches the copy of the buggy driver residing in memory. (If the driver is
ROM-based it first moves it to RAM, and then patches that.) Only two instructions
are modified, to save & restore more registers. This fix persists only until the
next reboot. If the patch is successfully applied the version number is increased
by 100.
NOTES:
Several bugs in version 2 (in System ≤6.03) of the video driver for Apple's
"Toby Frame Buffer" video card that affected use of the GetGamma call were
fixed in version 3 (in System 6.04), apparently in response to my bug report to
Apple.
The control/status-call parameter in a GDControl or GDStatus call specifies what
you want done. See Designing Cards and Drivers, 3rd Edition, Chapter 9. (A few
new calls are documented in the Display Device Driver Guide Developer Note.)
Note that sometime around 1990 Apple renamed some of the Control and Status
calls, giving them names that better reflect their function. I followed suit.
However, Apple neglected to update their book, Designing Cards and Drivers, now
in its 3rd edition. Fortunately, they define both names in their Video.h header
file. To avoid confusion, here are the equivalences. Note that "csc" stands for
"Control and Status Call"
Control call:
cscGrayPage = cscGrayScreen = 5
Status calls:
cscGetPageCnt = cscGetPages = 4
cscGetPageBase = cscGetBaseAddr = 5
Based on:
Inside Macintosh-II (Device Manager)
Designing Cards and Drivers, 3rd Edition, Chapter 9.
Display Device Driver Guide Developer Note on the January '94 Developer CD.
Tech Note 262: "Controlling Status Calls"
"GreyScale Information" from AppleLink "Apple Technical Info"
"Video Configuration ROM Software Specification" also from AppleLink,
in "Developer Tech Support:Macintosh:32 Bit QuickDraw"
NOTES:
Tom Busey (busey@indiana.edu) writes: "I'm using an 8-bit SuperMac ColorCard
that a used computer dealer gave me when I ordered a Toby card. I discovered
that the status and control calls for GDUncorrectedGamma return -17 and -18
respectively, which means that the driver doesn't support different gammas.
GDUncorrectedGamma returns an error message, but GDOpenWindow doesn't use the
error message or pass it back to the calling program. I'm wondering if there are
people using GDOpenWindow who think that they are getting a linear gamma but who
are actually getting an error message and not learning about it."
Tom's facts are right, but he needn't worry. A few video drivers, e.g.
Radius PowerView, and apparently the SuperMac ColorCard, don't implement gamma
tables at all, but the error from GDUncorrectedGamma is probably not a concern.
The real test is to run TimeVideo. TimeVideo does a write-and-read-back test of
the clut of your video card. If that test passes then you can safely conclude
that there's no gamma translation going on. So, as nearly every document in the
VideoToolbox says: run TimeVideo and read the report.
Here's how gamma tables work. According to the Apple manual (Designing
Cards and Drivers), the values in the rgb triplets that you supply in a
cscSetEntries call to the video driver are first translated via the gamma table
(each r,g, and b component separately) before being loaded into the CLUT
hardware table. Most drivers maintain a gamma table internally (in computer
memory) and allow you to get and set it via the cscGetGamma and cscSetGamma
calls. A few video drivers (Radius PowerView, SuperMac ColorCard) don't support
gamma tables. They load your rgb values directly into the CLUT, without
translation. However, for users of the VideoToolbox that's usually fine, since
that's exactly the behavior that we routinely request by calling
GDUncorrectedGamma. Of course, if you want to load a custom gamma table, other
than the identity transformation, then you'll be calling GDSetGamma, and you
should make sure it does not return an error. (Alas, the Radius PowerView driver
more or less ignores the cscSetGamma and cscGetGamma requests--GDGetGamma
returns a table that's all zeroes--but the driver fails to report an error. I
wrote to the Radius programmer a year ago, but they're no longer interested in
working on that product.)
Patrick Flanagan (flanagan@deakin.edu.au) writes: "Can I be 100% sure, if I
select "Special Gamma", in the Monitors Control Panel and choose "uncorrected
gamma" for a video card, that this is the same as using GDUncorrectedGamma?"
REPLY: That is what I would expect from reading all the relevant documentation.
However, to be 100% certain of anything I would want to check it myself.
TimeVideo calls GDUncorrectedGamma and then confirms that write-then-read tests
of the CLUT return what was written. (Gamma-table translation is only done on
the write, not undone on the read.) Thus TimeVideo will confirm for you that
after calling GDUncorrectedGamma your video card has what Apple calls an
"uncorrected" gamma table. However, TimeVideo does not check what you would get
by merely selecting "uncorrected" gamma in the Monitors Control Panel. It would
be reasonable to assume that you would get the same result since Monitors
interacts with the video driver by the same Control and Status Calls as
GDVideo.c does, so it must use the same cscSetGamma call as is used by
GDUncorrectedGamma.
HISTORY:
9/29/89 dgp added caution above that successive calls to SetEntries may be on one
frame.
11/21/89 dgp corrected mode list: 0x80... as pointed out by Chuck Stein
11/30/89 dgp added note above from Don Kelly.
Added cautionary note above about GDSetEntries().
3/1/90 dgp updated comments.
3/3/90 dgp included Video.h instead of defining VDSetEntryRecord and VDPageInfo.
3/20/90 dgp made compatible with MPW C.
3/23/90 dgp changed char to unsigned char in VDDefModeRec
and VDFlagRec to prevent undesirable sign extension.
4/2/90 dgp Deleted mode argument from GDGrayScreen().
7/28/90 dgp Fixed stack overflow in GDGrayScreen, by declaring SysEnvRec static.
10/20/90 dgp Apple has renamed the control and status calls, so I followed suit:
•GDGetPageBase replaces GDGetBaseAddr
•GDReset replaces GDInit
•GDGrayPage replace GDGrayScreen
•GDGetPageCnt replaces GDGetPages
2/12/91 dgp Discovered that the old bug in GDGrayPage is still present in System
6.05, so I removed the conditional around the bug fix. TestGDVideo
now works with: Mac II Video Card, Mac II Display Card (4•8),
Mac IIci Built-in Video, TrueVision NuVista, Mac IIsi Built-in Video.
7/22/91 dgp Changed definition of csGTable from GammaTblPtr to Ptr,
to conform with MPW C.
8/24/91 dgp Made compatible with THINK C 5.0.
10/22/91 dgp With help from Bart Farell, converted all functions headers to
Standard C style.
8/26/92 dgp added a few miscellaneous comments
In all routines, *baseAddrPtr is now set only if baseAddrPtr is not NULL.
10/10/92 dgp Added GDRestoreDeviceClut(). Removed obsolete support for THINK C 4.
11/30/92 dgp corrected error in comment documenting values of argument to GDSetGray().
Set flag to zero for color,1 for luminance-mapped gray.
12/9/92 dgp Enhanced GDRestoreDeviceClut(). Passing a NULL argument now requests
application to all devices. I only just learned that Apple's
RestoreDeviceClut() behaves in this way.
12/15/92 dgp Renamed GDRestoreDeviceClut to GDRestoreDeviceClut to be consistent
with Apple's capitalization of RestoreDeviceClut.
12/16/92 dgp Updated comments to be consistent with 3rd edition of Designing Cards
and Drivers. •Renamed myGDHandle to "device", which is easier to read.
Renamed GDLinearGamma to GDUncorrectedGamma, and generalized it
to work with any size DAC. For compatibility with old programs
VideoToolbox.h now has a #define statement making GDLinearGamma
an alias for GDUncorrectedGamma.
12/30/92 dgp Updated some of the comments. Eliminated Files.h.
12/30/92 dgp Use GDClutSize() to determine clut size.
1/4/93 dgp In GDClutSize, check for fixedType.
1/5/93 dgp In GDClutSize, only set last clut entry.
Added PatchMacIIciVideoDriver() to the end of this file, and
automatically invoke it the first time GDGetEntries is called.
1/6/93 dgp Cleaned up GDClutSize. It now seems to work correctly in the direct modes.
1/18/93 dgp Spruced up the documentation.
•Added all the code formerly in GDIdentify.c, but omitted the obsolete
function GDIdentify(), which simply printed GDName() and GDVersion.
•Enhanced GDGetEntries() to avoid calling drivers that are known
to crash, returning a statusErr instead.
•Recoded PatchMacIIciVideoDriver() so as not to call GetScreenDevice.c.
2/1/93 dgp Fixed endless loop in PatchMacIIciVideoDriver. Enhanced to deal with
ROM-based drivers as well.
2/5/93 dgp Expanded the documentation of GDSetEntries above, and supplied sample
code showing how to load the clut.
2/7/93 dgp Fixed endless loop in PatchMacIIciVideoDriver.
2/20/93 dgp Fixed bug in GDUncorrectedGamma() that was causing TestCluts to fail
for video devices that have nonstandard gamma tables.
3/18/93 dgp Fixed divide by zero error in GDClutSize.
4/6/93 dgp GDGetPageCnt() now sets *pagePtr argument only if no error occurs.
Deleted GDModeName(). Removed assumption, in all routines, that there
is any particular correspondence between the video mode number and
the pixel size. Added two new routines, GDPixelSize and GDType,
that return the pixelSize and gdType (i.e. fixedType, clutType, or
directType) of the device. Completely rewrote GDClutSize.
4/16/93 dgp Streamlined GDClutSize() to make it fast enough for routine use.
4/19/93 dgp Added GDNewLinearColorTable.
4/25/93 dgp Made CntrlParam automatic instead of static.
4/25/93 dgp Added GDColors(device) and GDPrintGammaTable(). Alphabetized the lists
of functions in the documentation above.
5/11/93 dgp Changed GDPrintGammaTable() to accept a simple file pointer instead of
an array of two file pointers.
7/7/93 dgp enhanced GDDacSize() to return 8 unless the gamma table is present and
reasonable.
7/29/94 dgp added GDNameStr().
9/5/94 dgp removed assumption in printf's that int==short.
10/25/94 dgp check for missing driver, (*device)->gdRefNum==0, and treat that case
just like device==NULL. Devices created by NewGWorld have no driver.
12/29/94 dgp added GDGetDisplayMode(), calls cscGetCurMode, based on Apple's new
Display Device Driver Guide Developer Note on the January '94 Developer CD.
1/15/94 dgp GDPixelSize if supplied with a merely device record, with no associated
device driver, then we just return (**(**device).gdPMap).pixelSize
3/22/95 dgp Added .Display_Video_Apple_DOMEMax to the list of drivers that don't
support cscGetEntries. Crash reported by Greg Jackson.
4/6/95 dgp Changed GDUncorrectedGamma() to pass a NULL gamma table pointer instead of
a linear gamma table, in the hopes that this might work even with the
few video drivers that don't fully support cscSetGamma.
*/
#include "VideoToolbox.h"
#include <assert.h>
#include <ROMDefs.h>
//#include <Displays.h>
#define dRAMBased 0x0040
#if !UNIVERSAL_HEADERS
/* these declarations appear in the latest Video.h */
struct VDSwitchInfoRec{
unsigned short csMode;
unsigned long csData;
unsigned short csPage;
Ptr csBaseAddr;
unsigned long csReserved;
};
typedef struct VDSwitchInfoRec VDSwitchInfoRec;
enum{cscGetCurMode=10};
enum{cscGetNextResolution=17};
typedef unsigned long DisplayModeID;
typedef unsigned short DepthMode;
struct VDResolutionInfoRec {
DisplayModeID csPreviousDisplayModeID; /* ID of the previous resolution in a chain */
DisplayModeID csDisplayModeID; /* ID of the next resolution */
unsigned long csHorizontalPixels; /* # of pixels in a horizontal line */
unsigned long csVerticalLines; /* # of lines in a screen */
Fixed csRefreshRate; /* Vertical Refresh Rate in Hz */
DepthMode csMaxDepthMode; /* 0x80-based number representing max bit depth */
unsigned long csReserved; /* Reserved */
unsigned long csReserved1; /* Reserved */
};
typedef struct VDResolutionInfoRec VDResolutionInfoRec;
typedef VDResolutionInfoRec *VDResolutionInfoPtr;
#endif
typedef struct VDFlagRec {
unsigned char flag;
char pad;
} VDFlagRec;
typedef struct VDDefModeRec{
unsigned char spID;
char pad;
} VDDefModeRec;
typedef struct {
short flags;
short blanks[3];
short open;
short prime;
short control;
short status;
short close;
Str255 name;
} VideoDriver;
VideoDriver *GDDriverAddress(GDHandle device);
typedef struct {
short csCode; // control code
short length; // total parameter block bytes
char param[]; // control call data
} ScrnCtl;
typedef struct {
short spDrvrHw; // Slot Manager ID
short slot; // Number of slot
long dCtlDevBase; // Start of device's address space
short mode; // screen characteristics
short flagMask; // Which flag bits are used
short flags; // device state: bit 0 = 0 = mono; bit 0 = 1 = color;
// bit 11 = 1 = startup device; bit 15 = 1 = active
short colorTable; // 'clut' id, default = -1
short gammaTable; // Selects color intensity, default (MacII) = -1
Rect globalRect; // global rectangle, main device topLeft = 0,0
short ctlCount; // total control calls
ScrnCtl ctl;
} Scrn;
typedef struct {
short scrnCount; // Total devices
Scrn scrn;
} Scrns; // 'scrn' resource
Scrn **GDGetScrn(GDHandle device);
OSErr GDGetNextResolution(GDHandle device,unsigned long previousDisplayModeID
,unsigned long *displayModeIDPtr,unsigned long *horizontalPixelsPtr
,unsigned long *verticalLinesPtr,Fixed *refreshRatePtr
,unsigned short *maxDepthModePtr);
OSErr GDGetDisplayMode(GDHandle device,unsigned long *displayModeIDPtr
,unsigned short *modePtr,unsigned short *pagePtr,Ptr *baseAddrPtr)
/*
It tells you the current display mode, etc., but only if the video driver
supports the Display Manager.
*/
{
VDSwitchInfoRec vdSwitchInfo;
int error;
if(device==NULL || (*device)->gdRefNum==0) return statusErr;
error=GDStatus((*device)->gdRefNum,cscGetCurMode,(Ptr) &vdSwitchInfo);
if(displayModeIDPtr!=NULL) *displayModeIDPtr=vdSwitchInfo.csData;
if(modePtr!=NULL) *modePtr=vdSwitchInfo.csMode;
if(pagePtr!=NULL) *pagePtr=vdSwitchInfo.csPage;
if(baseAddrPtr!=NULL) *baseAddrPtr=vdSwitchInfo.csBaseAddr;
return error;
}
OSErr GDGetNextResolution(GDHandle device,unsigned long previousDisplayModeID
,unsigned long *displayModeIDPtr,unsigned long *horizontalPixelsPtr
,unsigned long *verticalLinesPtr,Fixed *refreshRatePtr
,unsigned short *maxDepthModePtr)
/*
Tells you the next available displayModeID, after previousDisplayModeID.
This is a new call, introduced for PCI video drivers.
*/
{
VDResolutionInfoRec vdSwitchInfo;
int error;
if(device==NULL || (*device)->gdRefNum==0) return statusErr;
vdSwitchInfo.csPreviousDisplayModeID=previousDisplayModeID;
error=GDStatus((*device)->gdRefNum,cscGetNextResolution,(Ptr) &vdSwitchInfo);
if(displayModeIDPtr!=NULL) *displayModeIDPtr=vdSwitchInfo.csDisplayModeID;
if(horizontalPixelsPtr!=NULL) *horizontalPixelsPtr=vdSwitchInfo.csHorizontalPixels;
if(verticalLinesPtr!=NULL) *verticalLinesPtr=vdSwitchInfo.csVerticalLines;
if(refreshRatePtr!=NULL) *refreshRatePtr=vdSwitchInfo.csRefreshRate;
if(maxDepthModePtr!=NULL) *maxDepthModePtr=vdSwitchInfo.csMaxDepthMode;
return error;
}
OSErr GDSetPageDrawn(GDHandle device,short page)
// Select a page of video memory to draw into.
// Untested.
{
int error;
short flags;
Ptr baseAddr;
static long qD=-1;
if(qD==-1)Gestalt(gestaltQuickdrawVersion,&qD);
error=GDGetPageBase(device,page,&baseAddr);
if(!error){
if(qD>=gestalt32BitQD){
flags=GetPixelsState((**device).gdPMap);
LockPixels((**device).gdPMap);
}
(**(**device).gdPMap).baseAddr=baseAddr;
if(qD>=gestalt32BitQD){
GDeviceChanged(device);
SetPixelsState((**device).gdPMap,flags);
}
}
return error;
}
OSErr GDSetPageShown(GDHandle device,short page)
// Select a page of video memory for display.
// Untested.
{
int error;
short mode;
error=GDGetMode(device,&mode,NULL,NULL);
if(error)return error;
return GDSetMode(device,mode,page,NULL);
}
short GDType(GDHandle device)
// Returns what would normally be in (**device).gdType, for occasions when
// the GDevice record might be invalid because you called GDSetMode().
{
int error;
static ColorSpec white={255,0xffff,0xffff,0xffff},black={0,0,0,0};
error=GDSetEntries(device,0,0,&white);
if(!error)return clutType;
error=GDDirectSetEntries(device,0,0,&black);
if(!error)return directType;
return fixedType;
}
short GDPixelSize(GDHandle device)
// Returns what would normally be in (**(**device).gdPMap).pixelSize, for occasions
// when the GDevice record might be invalid because you called GDSetMode().
// Exception: if this is merely a device record, with no associated device driver,
// then we just return (**(**device).gdPMap).pixelSize
{
int error;
short mode;
if((**device).gdRefNum==0)return (**(**device).gdPMap).pixelSize;
error=GDGetMode(device,&mode,NULL,NULL);
return GDModePixelSize(device,mode);
}
short GDModePixelSize(GDHandle device,short mode)
// Returns the pixelSize associated with a given video mode.
// If you've changed the mode by calling GDSetMode and you're running a System
// older than 6.0.5 the answer may may be wrong for some of the newer video cards,
// because they have ideosynchratic associations of video mode and pixel size.
{
short j;
long version;
if(mode==(**device).gdMode)return (**(**device).gdPMap).pixelSize;
Gestalt(gestaltSystemVersion,&version);
if(version>=0x605){ // Need new Palette Manager for reliable answer.
for(j=5;j>=0;j--)if(mode==HasDepth(device,1<<j,0,0))return 1<<j;
} else return 1<<(mode&7); // Unreliable.
return 0;
}
Boolean GDHasMode(GDHandle device,short mode,short *pixelSizePtr,short *pagesPtr);
Boolean GDHasMode(GDHandle device,short mode,short *pixelSizePtr,short *pagesPtr)
// Returns 0 if no such mode, returns 1 if mode is available.
// If pixelSizePtr is not NULL, then sets *pixelSizePtr to pixelSize or -1 if unknown.
// If pagesPtr is not NULL, then sets *pagesPtr to pages.
{
short pixelSize,i,hasDepthWorks,pages;
int error;
long system;
Gestalt(gestaltSystemVersion,&system);
// On Mac IIci, Sys 6.07, HasDepth returns "mode" of 0x100 at all legal depths.
hasDepthWorks= system>=0x605 // New Palette Manager.
&& HasDepth(device,(**(**device).gdPMap).pixelSize,0,0)==(**device).gdMode;
if(hasDepthWorks){
for(i=0;i<6;i++){
pixelSize=1<<i;
if(mode!=HasDepth(device,pixelSize,0,0))continue;
if(pixelSizePtr!=NULL)*pixelSizePtr=pixelSize;
if(pagesPtr!=NULL){
error=GDGetPageCnt(device,mode,&pages);
if(error)pages=1;
*pagesPtr=pages;
}
return 1;
}
return 0;
}else{
// If HasDepth doesn't work properly then we can still find out whether the
// mode is available by asking the video driver for a page count, but
// I don't know of any discreet way to find out the pixelSize.
// Calling SetDepth would work, but that would irritate the user who has to
// watch and wait.
error=GDGetPageCnt(device,mode,&pages);
if(error)pages=0;
if(pagesPtr!=NULL)*pagesPtr=pages;
if(mode==(**device).gdMode)pixelSize=(**(**device).gdPMap).pixelSize;
else pixelSize=-1; // Unknown.
if(pixelSizePtr!=NULL)*pixelSizePtr=pixelSize;
return (pages>0);
}
}
short gdClutSizeTable[33]={0,1<<1,1<<2,0,1<<4,0,0,0,1<<8,0,0,0,0,0,0,0,1<<5
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1<<8};
long GDColors(GDHandle device)
// Returns the number of colors, in the current mode.
{
long colors=0;
short pixelSize;
if(device==NULL)return 2; // for compatibility with 1-bit QuickDraw
pixelSize=(**(**device).gdPMap).pixelSize;
if(pixelSize>=1 && pixelSize<=8)colors=1<<pixelSize;
if(pixelSize==16)colors=1L<<15;
if(pixelSize==32)colors=1L<<24;
return colors;
}
short GDClutSize(GDHandle device)
// Returns the number of entries in the clut, in the current mode.
{
short clutSize;
// Method 1. Estimate clut size from pixel size.
clutSize=gdClutSizeTable[(**(**device).gdPMap).pixelSize];
#if 0
// Method 2. Measure the clut's size by trying to load it.
if(GDType(device)==directType){
int error,i;
static const ColorSpec white={255,0xffff,0xffff,0xffff},black={0,0,0,0};
ColorSpec *table;
for(clutSize=256;clutSize>1;clutSize>>=1){
table=GDNewLinearColorTable(device);
if(table==NULL)PrintfExit("GDClutSize: out of memory.\n");
error=GDDirectSetEntries(device,0,clutSize-1,table);
DisposePtr((Ptr)table);
if(!error)break;
}
}
#endif
return clutSize;
}
short GDDacSize(GDHandle device)
// Figures out how many bits in the video dac. Answers for each device are cached.
{
short dacSize,i;
static short dacSizeCache[MAX_SCREENS];
static GDHandle deviceCache[MAX_SCREENS];
int error;
GammaTbl *gammaTblPtr=NULL;
for(i=0;i<MAX_SCREENS;i++)if(device==deviceCache[i])return dacSizeCache[i];
error=GDGetGamma(device,&gammaTblPtr); // Takes 200 µs.
if(error || gammaTblPtr==NULL || gammaTblPtr->gDataWidth==0)dacSize=8; // Oops. Take a guess.
else dacSize=gammaTblPtr->gDataWidth;
for(i=0;i<MAX_SCREENS;i++)if(NULL==deviceCache[i]){
deviceCache[i]=device;
dacSizeCache[i]=dacSize;
break;
}
return dacSize;
}
ColorSpec *GDNewLinearColorTable(GDHandle device)
// Creates a default table for use when gdType==directType.
{
short clutSize,i;
ColorSpec *table,*linearTable;
clutSize=GDClutSize(device);
table=linearTable=(ColorSpec *)NewPtr(clutSize*sizeof(linearTable[0]));
if(linearTable!=NULL)for(i=0;i<clutSize;i++){
table->rgb.red=table->rgb.green=table->rgb.blue
=(0xffffL*i+clutSize/2)/(clutSize-1);
table++;
}
return linearTable;
}
/*
Nominally equivalent to Apple's RestoreDeviceClut(), which is only available
since System 6.05. However, I find that Apple's routine sometimes does nothing,
whereas this routine always works. Passing a NULL argument causes restoration of
cluts of all video devices.
*/
OSErr GDRestoreDeviceClut(GDHandle device)
{
int error,lastError;
short count;
// If NULL, then recursively call ourselves for each device.
if(device==NULL){
lastError=0;
device=GetDeviceList();
while(device!=NULL) {
if(TestDeviceAttribute(device,screenDevice))
error=GDRestoreDeviceClut(device);
if(error)lastError=error;
device=GetNextDevice(device);
}
return lastError;
}
if(GDType(device)!=directType){
count=(**(**(**device).gdPMap).pmTable).ctSize;
error=GDSetEntries(device,0,count
,((**(**(**device).gdPMap).pmTable)).ctTable);
} else error=GDSetGamma(device,NULL);
return error;
}
GammaTbl **savedGammaTable[MAX_SCREENS];
OSErr GDSaveGamma(GDHandle device)
{
GammaTbl *gamma;
int error;
long size;
int i;
if(device==NULL || (*device)->gdType==fixedType)return 0;
i=GetScreenIndex(device);
if(i>=MAX_SCREENS)return 1;
error=GDGetGamma(device,&gamma);
if(error)return error;
size=gamma->gChanCnt*gamma->gDataCnt;
if(gamma->gDataWidth>8)size*=2;
size+=sizeof(GammaTbl)+gamma->gFormulaSize;
if(savedGammaTable[i]!=NULL){
DisposeHandle((Handle)savedGammaTable[i]);
savedGammaTable[i]=NULL;
}
error=PtrToHand(gamma,(Handle *)&savedGammaTable[i],size);
return error;
}
OSErr GDRestoreGamma(GDHandle device)
{
int i,error;
if((*device)->gdType==fixedType)return 0;
i=GetScreenIndex(device);
if(i>=MAX_SCREENS || savedGammaTable[i]==NULL)return 1;
error=GDSetGamma(device,*savedGammaTable[i]);
if(error)return error;
DisposeHandle((Handle)savedGammaTable[i]);
savedGammaTable[i]=NULL;
return 0;
}
OSErr GDGetDefaultGamma(GDHandle device,GammaTbl **gammaTbl)
// Looks for a default gamma table in both places that Apple says to look,
// but usually comes up empty handed.
{
int error,i,slot;
Scrn **scrn;
GammaTbl **gammaHandle,*gamma;
SpBlock spBlock;
Ptr ptr;
gammaHandle=NULL;
if(CountResources('gama')>0){// Any 'gama' resources available in System file?
// Check to see if 'scrn' resource in System file specifies a 'gama' resource.
scrn=GDGetScrn(device);
if(scrn!=NULL && (**scrn).gammaTable!=-1)
gammaHandle=(GammaTbl **)GetResource('gama',(**scrn).gammaTable);
DisposeHandle((Handle)scrn);
}
if(gammaHandle!=NULL){
// Got gamma table from 'gama' resource.
gamma=(GammaTbl *)NewPtr(GetHandleSize((Handle)gammaHandle));
if(gamma==NULL)return MemError();
BlockMove(*gammaHandle,gamma,GetHandleSize((Handle)gammaHandle));
DisposeHandle((Handle)gammaHandle);
}else{
// Try to get this device's default gamma table from Slot Manager.
spBlock.spSlot=slot=GetDeviceSlot(device);
spBlock.spID=0;
spBlock.spExtDev=0;
spBlock.spTBMask=3; // match only spCategory and spCType
spBlock.spCategory=catDisplay;
spBlock.spCType=typeVideo; // this might be too restrictive, excludes LCD
do{
error=SNextTypeSRsrc(&spBlock);
if(error)return error;
}while(spBlock.spSlot!=slot || spBlock.spRefNum!=(**device).gdRefNum);
if(0){
// Print sResource name
spBlock.spID=sRsrcName;
error=SGetCString(&spBlock);
printf("Slot resource “%s”\n",spBlock.spResult);
if(error)return error;
DisposePtr((Ptr)spBlock.spResult);
}
// Look for gamma directory. Unfortunately many video devices don't have one.
spBlock.spID=sGammaDir;
error=SFindStruct(&spBlock);
if(error)return error;
// Retrieve default gamma table
spBlock.spID=0x80;
error=SGetBlock(&spBlock);
if(error)return error;
gamma=(GammaTbl *)spBlock.spResult;
ptr=(Ptr)spBlock.spResult+6;
if(0)printf("Gamma table “%s”, %ld bytes\n",ptr,GetPtrSize((Ptr)gamma));
ptr+=strlen(ptr)+1; // table is just past string
ptr=(Ptr)((long)(ptr+1)&~1L); // round up to even address
i=ptr-(Ptr)gamma;
BlockMove(ptr,(Ptr)gamma,GetPtrSize((Ptr)gamma)-i);
SetPtrSize((Ptr)gamma,GetPtrSize((Ptr)gamma)-i);
}
*gammaTbl=gamma;
return 0;
}
Scrn **GDGetScrn(GDHandle device)
// Returns handle to a copy of the specific scrn resource for this device,
// or NULL if none.
{
int error=0,i,j;
Scrns **scrns;
Scrn *scrn,**scrnHandle;
ScrnCtl *ctl;
int scrnCount;
long size;
scrns=(Scrns **)GetResource('scrn',0);
if(ResError())return NULL;
HLockHi((Handle)scrns);
scrnCount=(**scrns).scrnCount;
scrn=&(**scrns).scrn;
for(i=0;i<scrnCount;i++){
if(0 && scrn->slot==GetDeviceSlot(device)){
printf("Slot %d,",(int)scrn->slot);
printf("mode %d,",(int)scrn->mode);
printf("colorTable %d,",(int)scrn->colorTable);
printf("gammaTable %d,",(int)scrn->gammaTable);
printf("%d control calls:",(int)scrn->ctlCount);
}
ctl=&scrn->ctl;
for(j=0;j<scrn->ctlCount;j++){
if(0 && scrn->slot==GetDeviceSlot(device))printf(" %d",(int)ctl->csCode);
ctl=(ScrnCtl *)((long)(ctl+1)+ctl->length);
}
if(0 && scrn->slot==GetDeviceSlot(device))printf("\n");
size=(long)ctl-(long)scrn;
if(scrn->slot==GetDeviceSlot(device))break;
scrn=(Scrn *)ctl;
}
if(i<scrnCount){
if(error)return NULL;
scrnHandle=(Scrn **)NewHandle(size);
BlockMove(scrn,*scrnHandle,size);
}else scrnHandle=NULL;
ReleaseResource((Handle)scrns);
return scrnHandle;
}
OSErr GDUncorrectedGamma(GDHandle device)
/*
Loads a linear gamma table into the specified video device, to defeat the
driver's attempt to do gamma correction.
According to Designing Cards and Drivers, 3rd edition, passing a NULL
GammaTblPtr instructs the driver to create a linear table.
*/
{
int error,i;
GammaTbl *gamma=NULL;
char *gData;
if(0){
/*
The the following code first examines the current
gamma table, and, if it's a plain vanilla table, as described in Designing Cards
and Drivers, then we write-in the desired linear table. If it's fancy (or if the
driver doesn't support GDGetGamma) then we pass a NULL pointer, letting the driver
create the new table. Hopefully this will work with all drivers.
*/
error=GDGetGamma(device,&gamma);
if(!error && gamma->gVersion==0 && gamma->gChanCnt==1 && gamma->gDataWidth<=8){
// Overwrite the standard table
gData = (char *)&gamma->gFormulaData+gamma->gFormulaSize;
for(i=0;i<gamma->gDataCnt;i++)gData[i]=i;
}else{
// A fancy table implies a new driver, so let it do the work.
gamma=NULL;
}
}
error=GDSetGamma(device,gamma);
return error;
}
/* KillIO doesn't do anything, so I didn't bother to implement it. */
OSErr GDPrintGammaTable(FILE *o,GDHandle device)
{
unsigned char *byte;
unsigned short *word;
int error,i,j,identity;
GammaTbl *gamma;
if((**device).gdType==fixedType)return statusErr;
error=GDGetGamma(device,&gamma);
if(error){
fprintf(o,"GetGamma: GDGetGamma() error %d\n",error);
if(error==statusErr)
fprintf(o,"The video driver doesn't support this call.\n");
return error;
}
byte=(unsigned char *)gamma->gFormulaData+gamma->gFormulaSize;
word=(unsigned short *)byte;
identity=1;
if(gamma->gDataWidth<=8)
for(i=0;i<gamma->gDataCnt;i++)identity &= (i==byte[i]);
else
for(i=0;i<gamma->gDataCnt;i++)identity &= (i==word[i]);
if(identity){
fprintf(o,"Gamma Table: identity transformation\n");
}else{
fprintf(o,"Gamma Table:\n");
fprintf(o,"at 0x%lx,gDataWidth %d,gDataCnt %d,gVersion %d,gType %d,gFormulaSize %d,gChanCnt %d\n"
,gamma,(int)gamma->gDataWidth,(int)gamma->gDataCnt,(int)gamma->gVersion
,(int)gamma->gType,(int)gamma->gFormulaSize,(int)gamma->gChanCnt);
for(i=0;i<gamma->gDataCnt;i+=64) {
fprintf(o,"%3d: ",i);
if(gamma->gDataWidth<=8)
for(j=0;j<16;j++) fprintf(o," %3u",byte[i+j]);
else
for(j=0;j<16;j++) fprintf(o," %3u",word[i+j]);
fprintf(o,"\n");
}
}
return 0;
}
OSErr GDReset(GDHandle device, short *modePtr, short *pagePtr, Ptr *baseAddrPtr)
/*
Initialize the video card to its startup state, usually 1 bit per pixel. Returns
the parameters of that state.
*/
{
VDPageInfo myVDPageInfo;
int error;
if(device==NULL || (*device)->gdRefNum==0) return controlErr;
myVDPageInfo.csMode= *modePtr;
myVDPageInfo.csPage= *pagePtr;
error=GDControl((*device)->gdRefNum,cscReset,(Ptr) &myVDPageInfo);
*modePtr=myVDPageInfo.csMode;
*pagePtr=myVDPageInfo.csPage;
if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
return error;
}
OSErr GDSetMode(GDHandle device,short mode,short page,Ptr *baseAddrPtr)
{
VDPageInfo myVDPageInfo;
int error;
if(device==NULL || (*device)->gdRefNum==0) return controlErr;
myVDPageInfo.csMode=mode;
myVDPageInfo.csPage=page;
error=GDControl((*device)->gdRefNum,cscSetMode,(Ptr) &myVDPageInfo);
if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
return error;
}
OSErr GDSetEntriesByType(GDHandle device,short start,short count,ColorSpec *table)
// Calls GDSetEntries or GDDirectSetEntries or nothing, as appropriate.
// Assumes that the GDevice record is valid, i.e. that the user has not
// called GDSetMode.
{
switch((*device)->gdType){
case fixedType:
return statusErr;
case clutType:
return GDSetEntries(device,start,count,table);
case directType:
return GDDirectSetEntries(device,start,count,table);
}
return 1;
}
OSErr GDSetEntriesByTypeHighPriority(GDHandle device,short start,short count
,ColorSpec *table)
{
char priority;
int error;
priority=7;
SwapPriority(&priority);
error=GDSetEntriesByType(device,start,count,table);
SwapPriority(&priority);
return error;
}
OSErr GDSetEntries(GDHandle device,short start,short count,ColorSpec *table)
{
VDSetEntryRecord vDSetEntryRecord;
int error;
if(device==NULL || (*device)->gdRefNum==0) return controlErr;
vDSetEntryRecord.csStart=start;
vDSetEntryRecord.csCount=count;
vDSetEntryRecord.csTable=table;
error=GDControl((*device)->gdRefNum,cscSetEntries,(Ptr) &vDSetEntryRecord);
return error;
}
OSErr GDSetGamma(GDHandle device, GammaTbl *gamma)
{
int error;
VDGammaRecord myVDGammaRecord;
myVDGammaRecord.csGTable=(Ptr)gamma;
error=GDControl((*device)->gdRefNum,cscSetGamma,(Ptr) &myVDGammaRecord);
return error;
}
OSErr GDGrayPage(GDHandle device,short page)
/*
Called "GrayScreen" in Designing Cards and Drivers, 3rd Ed. Fills the specified
page with gray, i.e. the dithered desktop pattern. I'm not aware of any
particular advantage in using this instead of FillRect().
Designing Cards and Drivers, 3rd Edition, Chapter 9, says that for direct
devices, i.e. >8 bit pixels, the driver will also load a linear,
gamma-corrected, gray color table.
Contrary to the documentation, version 2 (in System 6.03) of the video driver
for Apple's old video card requires that one supply the current mode as well.
Supplying a garbage mode screwed up the screen and soon hung the software. So
this code first obtains the current mode, and then supplies it in the GrayPage
Control call.
*/
{
VDPageInfo myVDPageInfo;
int error;
/* The rest of the arguments are used soley for the bug fix */
short mode=0; /* should be ignored, but isn't */
short actualPage; /* ignored */
Ptr baseAddr; /* ignored */
if(device==NULL || (*device)->gdRefNum==0) return controlErr;
/* Work around driver bug: get the mode */
error=GDGetMode(device,&mode,&actualPage,&baseAddr);
if(error)return error;
myVDPageInfo.csMode=mode;
myVDPageInfo.csPage=page;
error=GDControl((*device)->gdRefNum,cscGrayPage,(Ptr) &myVDPageInfo);
return error;
}
OSErr GDSetGray(GDHandle device,Boolean flag)
/*
Tells the driver whether you want colors (flag==0), or prefer that all colors be
mapped to luminance-equivalent gray tones? (flag==1).
*/
{
VDFlagRec myVDFlagRec;
int error;
if(device==NULL || (*device)->gdRefNum==0) return controlErr;
myVDFlagRec.flag=flag;
error=GDControl((*device)->gdRefNum,cscSetGray,(Ptr) &myVDFlagRec);
return error;
}
OSErr GDSetInterrupt(GDHandle device,Boolean flag)
/*
Set flag to 1 to enable VBL interrupts of this card, or 0 to disable.
I don't know when it's appropriate to call this.
*/
{
VDFlagRec myVDFlagRec;
int error;
if(device==NULL || (*device)->gdRefNum==0) return controlErr;
myVDFlagRec.flag=flag;
error=GDControl((*device)->gdRefNum,cscSetInterrupt,(Ptr) &myVDFlagRec);
return error;
}
OSErr GDDirectSetEntries(GDHandle device,short start,short count,ColorSpec *table)
/*
If your pixel depth is >8 then the cscSetEntries Control call is disabled, and you use
cscDirectSetEntries instead. Except for that, GDDirectSetEntries is identical to GDSetEntries.
*/
{
VDSetEntryRecord vDSetEntryRecord;
int error;
if(device==NULL || (*device)->gdRefNum==0) return controlErr;
vDSetEntryRecord.csStart=start;
vDSetEntryRecord.csCount=count;
vDSetEntryRecord.csTable=table;
error=GDControl((*device)->gdRefNum,cscDirectSetEntries,(Ptr) &vDSetEntryRecord);
return error;
}
OSErr GDSetDefaultMode(GDHandle device,short mode)
/*
Supposedly, you tell it what mode you want to start up with when you reboot.
(I've never been able to get this to work. No error and no effect. Perhaps I've
misunderstood its purpose.)
*/
{
VDDefModeRec myVDDefModeRec;
int error;
if(device==NULL || (*device)->gdRefNum==0) return controlErr;
myVDDefModeRec.spID=mode;
error=GDControl((*device)->gdRefNum,cscSetDefaultMode,(Ptr) &myVDDefModeRec);
return error;
}
OSErr GDGetMode(GDHandle device,short *modePtr,short *pagePtr,Ptr *baseAddrPtr)
/*
It tells you the current mode, page of video memory, and the base address of that
page.
*/
{
VDPageInfo myVDPageInfo;
int error;
if(device==NULL || (*device)->gdRefNum==0) return statusErr;
error=GDStatus((*device)->gdRefNum,cscGetMode,(Ptr) &myVDPageInfo);
if(modePtr!=NULL)*modePtr=myVDPageInfo.csMode;
if(pagePtr!=NULL)*pagePtr=myVDPageInfo.csPage;
if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
return error;
}
OSErr GDGetEntries(GDHandle device,short start,short count,ColorSpec *table)
/*
This is much as you'd expect after reading GDSetEntries above. Note that unless
the gamma table is linear, the values returned may not be the same as those
originally passed by GDSetEntries. So call GDUncorrectedGamma first. Note that
version 2 (in System 6.03) of the video driver for Apple's old video card had a
bug and did not support "indexed" mode, i.e. start==-1. This is fixed in System
6.05. Apple's .Display_Video_Apple_RBV1 v. 0 video driver (built-in to Mac IIci)
crashes if you attempt to make this call. However, that's a thing of the past,
because we now first patch the driver to fix the bug. Try the demo TimeVideo.
*/
{
VDSetEntryRecord vDSetEntryRecord;
int error;
static int bugsPatched=0;
unsigned char *name;
int version;
if(device==NULL || (*device)->gdRefNum==0)return statusErr;
if(!bugsPatched){
PatchMacIIciVideoDriver();
bugsPatched=1;
}
// Contrary to Apple's rules, these drivers crash if we attempt
// a getEntries call. So let's be polite and instead simply return
// an error message indicating that this call is not available.
name=GDName(device);
version=GDVersion(device);
if(EqualString(name,"\p.Display_Video_Apple_RBV1",1,1) && version==0)return statusErr;
if(EqualString(name,"\p.Color_Video_Display",1,1) && version==9288)return statusErr;
if(EqualString(name,"\p.Display_Video_Apple_DOMEMax",1,1) && version==2)return statusErr;
vDSetEntryRecord.csStart=start;
vDSetEntryRecord.csCount=count;
vDSetEntryRecord.csTable=table;
error=GDStatus((*device)->gdRefNum,cscGetEntries,(Ptr) &vDSetEntryRecord);
return error;
}
OSErr GDGetPageCnt(GDHandle device,short mode,short *pagesPtr)
/*
Called "GetPages" in Designing Cards and Drivers, 3rd Ed. You tell it what mode
you're interested in. It tells you how many pages of video ram are available.
*/
{
VDPageInfo myVDPageInfo;
int error;
if(device==NULL || (*device)->gdRefNum==0) return statusErr;
myVDPageInfo.csMode=mode;
error=GDStatus((*device)->gdRefNum,cscGetPageCnt,(Ptr) &myVDPageInfo);
if(!error)*pagesPtr=myVDPageInfo.csPage;
return error;
}
OSErr GDGetPageBase(GDHandle device,short page,Ptr *baseAddrPtr)
/*
Called "GetBaseAddr" in Designing Cards and Drivers, 3rd Ed. You tell it what
page of video memory you're interested in (in the current video mode). It tells
you the base address of that page.
*/
{
VDPageInfo myVDPageInfo;
int error;
if(device==NULL || (*device)->gdRefNum==0) return statusErr;
myVDPageInfo.csPage=page;
error=GDStatus((*device)->gdRefNum,cscGetPageBase,(Ptr) &myVDPageInfo);
if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
return error;
}
OSErr GDGetGray(GDHandle device,Boolean *flagPtr)
// It tells you what the flag is set to. Do you want colors? (flag==0) Or do you
// want all colors mapped to luminance-equivalent gray tones? (flag==1).
{
VDFlagRec myVDFlagRec;
int error;
if(device==NULL || (*device)->gdRefNum==0) return statusErr;
error=GDStatus((*device)->gdRefNum,cscGetGray,(Ptr) &myVDFlagRec);
*flagPtr=myVDFlagRec.flag;
return error;
}
OSErr GDGetInterrupt(GDHandle device,Boolean *flagPtr)
// Get flag. 1 if VBL interrupts of this card are enabled. 0 if disabled.
{
VDFlagRec myVDFlagRec;
int error;
if(device==NULL || (*device)->gdRefNum==0) return statusErr;
error=GDStatus((*device)->gdRefNum,cscGetInterrupt,(Ptr) &myVDFlagRec);
*flagPtr=myVDFlagRec.flag;
return error;
}
OSErr GDGetGamma(GDHandle device,GammaTbl **myGammaTblHandle)
/*
Returns a pointer to the Gamma table in the specified video device. (I.e. you
pass it a pointer to your pointer, a handle, which it uses to load your
pointer.)
Note that version 2 (in System ≤6.03) of the video driver for Apple's old video
card does not support this call due to a bug in the driver code. The later
versions of the driver (3, 4, and 5, in System 6.04 and later) work correctly.
*/
{
int error;
VDGammaRecord myVDGammaRecord;
if(device==NULL || (*device)->gdRefNum==0) return statusErr;
myVDGammaRecord.csGTable=NULL; // default address is NULL
error=GDStatus((*device)->gdRefNum,cscGetGamma,(Ptr) &myVDGammaRecord);
*myGammaTblHandle=(GammaTblPtr)myVDGammaRecord.csGTable;
return error;
}
OSErr GDGetDefaultMode(GDHandle device,short *modePtr)
// It tells you what the default mode is. I'm not sure what this means.
{
VDDefModeRec myVDDefModeRec;
int error;
if(device==NULL || (*device)->gdRefNum==0) return statusErr;
error=GDStatus((*device)->gdRefNum,cscGetDefaultMode,(Ptr) &myVDDefModeRec);
*modePtr=(unsigned char)myVDDefModeRec.spID;
return error;
}
OSErr GDControl(int refNum,int csCode,Ptr csParamPtr)
// Uses low-level PBControl() call to implement a "Control()" call that works!
// I don't know why this wasn't discussed in Apple Tech Note 262.
{
CntrlParam control;
int error;
control.ioCompletion=NULL;
control.ioCRefNum=refNum;
control.csCode=csCode;
*((Ptr *) &control.csParam[0]) = csParamPtr;
error=PBControl((ParmBlkPtr) &control,0);
return error;
}
OSErr GDStatus(int refNum,int csCode,Ptr csParamPtr)
// Uses low-level PBStatus() call to implement a "Status()" call that works! The
// need for this is explained in Apple Tech Note 262, which was issued in response
// to my bug report in summer of '89.
{
CntrlParam control;
int error;
control.ioCompletion=NULL;
control.ioCRefNum=refNum;
control.csCode=csCode;
*((Ptr *) &control.csParam[0]) = csParamPtr;
error=PBStatus((ParmBlkPtr) &control,0);
return error;
}
#if 0
/*
From: absurd@apple.apple.com (Tim Dierks, software saboteur)
Date: Fri, 20 Nov 1992 00:38:14 GMT
Organization: MacDTS Marauders
Here's the right way to get the slot number of a monitor, given its
GDevice: (as a bonus, this also gets the name of the card in
question; to just get the slot, chop off all the lines after the
*slot = ... line.
*/
OSErr GetSlotAndName(GDHandle gDev,short *slot,char *name)
{ OSErr err;
short refNum;
SpBlock spb;
refNum = (**gDev).gdRefNum; // video driver refNum for this GDevice
*slot = (**(AuxDCEHandle)GetDCtlEntry(refNum)).dCtlSlot;
// slot in which this video card sits
spb.spSlot = *slot; // In the slot we're interested in
spb.spID = 0;
spb.spExtDev = 0;
spb.spCategory = 1; // Get the board sResource
spb.spCType = 0;
spb.spDrvrSW = 0;
spb.spDrvrHW = 0;
spb.spTBMask = 0;
err = SNextTypeSRsrc(&spb);
if (err)return err;
spb.spID = 2; // Getting the description string
// spSPointer was set up by SNextTypesResource
err = SGetCString(&spb);
if (err)return err;
strcpy(name,(char *)spb.spResult); // Get the returned string
DisposPtr((Ptr)spb.spResult); // Undocumented; we have to dispose of it
c2pstr(name);
return noErr;
}
#endif
char *GDCardName(GDHandle device)
/*
Returns the address of a C string containing the name of the video card. You
should call DisposPtr() on the returned address when you no longer need it.
Takes about 1.5 ms; I don't know why Apple's slot routines are so slow. This
routine sets up the flags in the SNextTypeSRsrc() call quite differently from
the above example, but I don't know if that matters, since I've been using this
routine for a year or so without any problems.
*/
{
AuxDCE **auxDCEHandle;
SpBlock SPB,SPB1;
if(device==NULL || (*device)->gdRefNum==0)return "";
auxDCEHandle = (AuxDCE **) GetDCtlEntry((*device)->gdRefNum);
SPB.spSlot = (**auxDCEHandle).dCtlSlot;
SPB.spCategory = 0;
SPB.spCType = 0;
SPB.spDrvrSW = 0;
SPB.spDrvrHW = 1;
SPB.spTBMask = 0xf;
SPB.spID = 0;
SPB.spExtDev = 0;
if(SNextTypeSRsrc(&SPB) == noErr) {
SPB1.spsPointer = SPB.spsPointer;
SPB1.spID = 2;
SGetCString(&SPB1);
return (char *)SPB1.spResult;
}
else return "";
}
unsigned char *GDName(GDHandle device)
// Returns a pointer to the name of the driver (a pascal string).
{
VideoDriver *videoDriverPtr;
if(device==NULL || (*device)->gdRefNum==0)return "\p";
videoDriverPtr=GDDriverAddress(device);
return videoDriverPtr->name;
}
char *GDNameStr(GDHandle device)
// Returns the driver name as a C string.
{
unsigned char *sp;
static char name[32];
sp=GDName(device);
memcpy(name,sp,sp[0]+1);
return p2cstr((unsigned char *)name);
}
int GDVersion(GDHandle device)
// Returns the version number of the driver. From the first word-aligned word after
// the name string.
{
int version;
unsigned char *bytePtr;
if(device==NULL || (*device)->gdRefNum==0)return 0;
bytePtr=GDName(device);
bytePtr += 1+bytePtr[0]; /* go to end of Pascal string */
bytePtr = (unsigned char *)((long)(bytePtr+1) & ~1); // round up to word boundary
version = *(short *)bytePtr;
return version;
}
VideoDriver *GDDriverAddress(GDHandle device)
// Returns a pointer to the driver, whether in ROM or RAM.
{
AuxDCE **auxDCEHandle;
VideoDriver *videoDriverPtr;
if(device==NULL || (*device)->gdRefNum==0)return NULL;
auxDCEHandle = (AuxDCE **) GetDCtlEntry((*device)->gdRefNum);
if((**auxDCEHandle).dCtlFlags & dRAMBased){
/* RAM-based driver. */
videoDriverPtr=*(VideoDriver **) (**auxDCEHandle).dCtlDriver;
}
else{
/* ROM-based driver. */
videoDriverPtr=(VideoDriver *) (**auxDCEHandle).dCtlDriver;
}
return videoDriverPtr;
}
/*
ROUTINE: PatchMacIIciVideoDriver
PURPOSE:
It is unlikely that you will need to call this explicitly, because it is called
automatically by GDGetEntries the first time it is invoked, and the sole purpose
of this routine is to fix a driver bug that would cause a crash when called by
GDGetEntries.
The Mac IIci built-in video driver (.Display_Video_Apple_RBV1 driver, version 0)
has a bug that causes it to crash if you try to do a getEntries Status request.
PatchMacIIciVideoDriver() will find and patch the memory-resident copy of the
buggy driver. Only two instructions are modified, to save and restore more
registers. This fix persists only until the next reboot. If the patch is
successfully applied the version number is increased from 0 to 100, to
distinguish it from versions 0 and 1.
A returned value of 1 indicates success: the needed patch was applied, either now
or previously. A return value of 0 indicates no patch was needed.
This patch is based on a comparison of the version 0 and 1 drivers used by the
Mac IIci and IIsi, respectively. The patch changes a pair of save and restore
operations (MOVEM.L to and from the stack) to save and restore registers D1 and
A1 as well as D4, A3, and A4. There are many other differences between versions
0 and 1 of the driver, but this change is enough to keep the version 0 driver
from crashing when we attempt to read the clut by calling GDGetEntries.
The only change that the Mac operating system could possibly notice is that,
when we're done patching, we set the handle to be non-purgeable, since purging
and reloading the driver would eliminate the patch.
edward_de_Jong@bmug.org and Robert Savoy (SAVOY@RISVAX.ROWLAND.ORG) reported
that their Mac IIci computers' built-in video driver is ROM-based, so
PatchMacIIciVideoDriver was enhanced to deal with a ROM-based driver, by copying
the driver into RAM, making that the active driver, and patching it. Robert
Savoy reports that it works fine.
An alternative, permanent, solution is described in the file "Video synch":
upgrading to Apple's bug-free version 1 of the driver. However, that solution
only works if the Mac IIci has more than one monitor.
*/
int PatchMacIIciVideoDriver(void)
{
GDHandle device;
short *w;
AuxDCE **auxDCEHandle;
Handle handle;
enum{badVersion=0};
int error;
long value;
error=Gestalt(gestaltQuickdrawVersion,&value);
if(error || value<gestalt8BitQD)return 0; // need 8-bit quickdraw
device = GetDeviceList();
while(1) {
if(device==NULL || (*device)->gdRefNum==0)return 0;
if (!TestDeviceAttribute(device,screenDevice)
|| !TestDeviceAttribute(device,screenActive)){
device=GetNextDevice(device);
continue;
}
if(EqualString("\p.Display_Video_Apple_RBV1",GDName(device),1,1))
switch(GDVersion(device)){
case badVersion:
break;
case badVersion+100: // already patched
return 1;
default:
return 0;
}else{
device=GetNextDevice(device);
continue;
}
break;
}
auxDCEHandle = (AuxDCE **) GetDCtlEntry((*device)->gdRefNum);
// Move ROM-based driver into RAM.
if(!((**auxDCEHandle).dCtlFlags & dRAMBased)){
long bytes;
VideoDriver *driver;
driver=(VideoDriver *)(**auxDCEHandle).dCtlDriver;
// Sometimes the word preceding the driver in ROM seems to be the
// driver size, but not always, e.g. the built-in driver on the Mac IIsi.
bytes=*((short *)driver-1);
// Driver size unknown, guessing (generously) at twice the highest offset.
bytes=driver->open;
if(bytes<driver->prime)bytes=driver->prime;
if(bytes<driver->control)bytes=driver->control;
if(bytes<driver->status)bytes=driver->status;
if(bytes<driver->close)bytes=driver->close;
bytes*=2;
// We know the Mac IIci driver size to be 1896
// when ROM version is 124 rev. 1, but who knows for later ROMs?
//bytes=1896;
handle=NewHandleSys(bytes);
if(handle==NULL)return 0; // Insufficient room on System heap.
HLockHi(handle);
BlockMove((Ptr)driver,*handle,bytes);
(**auxDCEHandle).dCtlDriver=(Ptr)handle;
(**auxDCEHandle).dCtlFlags |= dRAMBased;
}
// Patch RAM-based driver.
handle=(Handle)(**auxDCEHandle).dCtlDriver;
w=*(short **)handle;
if(w[0x51e/2]!=0x818 || w[0x590/2]!=0x1810 || w[0x2c/2]!=badVersion){
printf("PatchMacIIciVideoDriver error.\n");
return 0;
}
w[0x51e/2]=0x4858;
w[0x590/2]=0x1a12;
w[0x2c/2]+=100; // Change version number.
if(w[0x51e/2]!=0x4858 || w[0x590/2]!=0x1a12){
printf("PatchMacIIciVideoDriver error.\n");
return 0;
}
HNoPurge(handle);
return 1;
}